package com.school.information.core.security.filter;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.school.information.core.IResultStatus;
import com.school.information.core.constant.TokenConstant;
import com.school.information.core.security.authen.WechatAppAuthenticationToken;
import com.school.information.core.security.entity.SecurityUser;
import com.school.information.core.security.entity.WechatAppUser;
import com.school.information.core.service.RedisService;
import com.school.information.enums.result.JwtResultEnum;
import com.school.information.utils.JwtUtil;
import com.school.information.utils.ResultUtil;
import com.school.information.utils.WebUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

/*
 * JWT认证过滤器
 */
@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Resource
    private RedisService redisService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        log.info("##jwtAuthen认证处理开始##");
        // 避免此前曾经存入过用户信息，后续即使没有携带JWT，在Spring Security仍保存有上下文数据（包括用户信息）
        SecurityContextHolder.clearContext();
        // 后台pc端管理用户登录验证
        if (StringUtils.isNotBlank(request.getHeader("token"))) {
            String token = request.getHeader("token");
            // 校验token
            if (!JwtUtil.isSigned(token)) {
                log.error("##token格式错误，token非jwt生产的token，即非法token，直接进行重新登录，无需刷新token处理");
//                throw new ServiceException(JwtResultEnum.JWT_SIGN_ERROR);
                handleErrorMsg(response, JwtResultEnum.JWT_GET_TOKEN_ERROR);
                return;
            }
            boolean verifyFlag = JwtUtil.verify(token);
            if (verifyFlag) {
                // 获取用户id、手机号信息
                String userId = JwtUtil.getValue(token, TokenConstant.JWT_USER_ID);
                String userPhone = JwtUtil.getValue(token, TokenConstant.JWT_USER_PHONE);
                String loginDevice = JwtUtil.getValue(token, TokenConstant.LOGIN_DEVICE);
                SecurityUser securityUser = (SecurityUser) redisService.get(TokenConstant.LOGIN_USER_REDIS_KEY + loginDevice + "_" + userId + "_" + userPhone);
                if (ObjectUtil.isEmpty(securityUser)) {
                    log.error("##redis中不存在用户信息，即token已过期，需重新登录");
//                    throw new ServiceException(JwtResultEnum.JWT_SIGN_ERROR);
                    handleErrorMsg(response, JwtResultEnum.JWT_SIGN_ERROR);
                    return;
                }
                // 登录成功 直接放行
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(securityUser,
                        null, securityUser.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                filterChain.doFilter(request, response);
                return;
            } else {
                log.error("##jwtToken解析失败");
//                throw new ServiceException(JwtResultEnum.JWT_TOKEN_ERROR);
                handleErrorMsg(response, JwtResultEnum.JWT_TOKEN_ERROR);
                return;
            }
        } else if (StringUtils.isNotBlank(request.getHeader("wechatToken"))) {
            // 小程序端客户登录验证
            String token = request.getHeader("wechatToken");
            // 校验token
            if (!JwtUtil.isSigned(token)) {
                log.error("##token格式错误，token非jwt生产的token，即非法token，直接进行重新登录，无需刷新token处理");
//                throw new ServiceException(JwtResultEnum.JWT_SIGN_ERROR);
                handleErrorMsg(response, JwtResultEnum.JWT_GET_TOKEN_ERROR);
                return;
            }
            boolean verifyFlag = JwtUtil.verify(token);
            if (verifyFlag) {
                // 获取小程序id信息
                String userId = JwtUtil.getValue(token, TokenConstant.JWT_USER_ID);
                String loginTime = JwtUtil.getValue(token, TokenConstant.LOGIN_TIME);
                String loginDevice = JwtUtil.getValue(token, TokenConstant.LOGIN_DEVICE_WECHAT);
                WechatAppUser wechatAppUser = (WechatAppUser) redisService.get(TokenConstant.LOGIN_USER_REDIS_KEY + loginDevice + "_" + userId + "_" + loginTime);
                if (ObjectUtil.isEmpty(wechatAppUser)) {
                    log.error("##redis中不存在用户信息，即token已过期，需重新登录");
//                    throw new ServiceException(JwtResultEnum.JWT_SIGN_ERROR);
                    handleErrorMsg(response, JwtResultEnum.JWT_SIGN_ERROR);
                    return;
                }
                // 登录成功 直接放行
                WechatAppAuthenticationToken authenticationToken = new WechatAppAuthenticationToken(wechatAppUser, null, null);
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                filterChain.doFilter(request, response);
                return;
            } else {
                log.error("##jwtToken解析失败");
//                throw new ServiceException(JwtResultEnum.JWT_TOKEN_ERROR);
                handleErrorMsg(response, JwtResultEnum.JWT_TOKEN_ERROR);
                return;
            }
        } else {
            // 没有token即表示不需要进行登录验证的url,若输入错误的url，肯定会是404，
            // 若输入正确的地址，且没有携带token，会在SecurityConfig中的authorizeHttpRequests进行登录请求验证
            // 若不允许登录放行 则返回401无权限
            filterChain.doFilter(request, response);
        }
    }

    public void handleErrorMsg(HttpServletResponse response, IResultStatus resultStatusVo) {
        WebUtils.renderString(response, JSON.toJSONString(ResultUtil.error(resultStatusVo)));
    }
}
